From 2446137e3cb13e175d9056415cb086ae5e891254 Mon Sep 17 00:00:00 2001 From: Jason Ish Date: Wed, 7 Jan 2026 09:23:09 -0600 Subject: [PATCH] [PATCH 3/4] dnp3: set a bound on the number of points per message 16384 is used as the max, but a configuration parameter has been provided. The reason for setting an upper bound is that bit flags can create a memory amplification as we parse them into individual data structures. Ticket: #8181 (cherry picked from commit 3a32bb5743c35afb3278a6448f7e9669512dbe92) Origin: upstream, https://github.com/OISF/suricata/commit/fdd79bdb14488244604729f1d68ca4bc60000dbd.patch Bug: https://redmine.openinfosecfoundation.org/issues/8181 Subject: Upstream fix for CVE-2026-22259 part 3 Gbp-Pq: Name CVE-2026-22259_3.patch --- doc/userguide/upgrade.rst | 9 +++++---- rules/dnp3-events.rules | 5 +++++ src/app-layer-dnp3.c | 33 ++++++++++++++++++++++++++------- src/app-layer-dnp3.h | 1 + suricata.yaml.in | 1 + 5 files changed, 38 insertions(+), 11 deletions(-) diff --git a/doc/userguide/upgrade.rst b/doc/userguide/upgrade.rst index ed4e59c7..6c990300 100644 --- a/doc/userguide/upgrade.rst +++ b/doc/userguide/upgrade.rst @@ -39,10 +39,11 @@ Upgrading to 7.0.14 (trixie-security 1:7.0.10-1~bpo13u3) Other Changes ~~~~~~~~~~~~~ -- ``dnp3`` has reduced the default maximum number of outstanding - transactions from 500 down to 32. A ``max-tx`` parameter has been - added to the ``dnp3`` parser for users that need a larger number of - in-flight transactions. +- ``dnp3`` has reduced the maximum number of open transactions from + 500 down to 32, and the maximum number of points per message from + unbounded to 16384. Configuration options, ``max-tx`` and + ``max-points`` have been added for users who may need to change + these defaults. Upgrading to 7.0.9 ------------------ diff --git a/rules/dnp3-events.rules b/rules/dnp3-events.rules index e4890f88..59e4c24d 100644 --- a/rules/dnp3-events.rules +++ b/rules/dnp3-events.rules @@ -24,3 +24,8 @@ alert dnp3 any any -> any any (msg:"SURICATA DNP3 Bad transport CRC"; \ # Unknown object. alert dnp3 any any -> any any (msg:"SURICATA DNP3 Unknown object"; \ app-layer-event:dnp3.unknown_object; classtype:protocol-command-decode; sid:2270004; rev:2;) + +# Too many points in a message. +alert dnp3 any any -> any any (msg:"SURICATA DNP3 Too many points in message"; \ + app-layer-event:dnp3.too_many_points; \ + classtype:protocol-command-decode; sid:2270005; rev:1;) diff --git a/src/app-layer-dnp3.c b/src/app-layer-dnp3.c index 15de9bbe..b1ad323e 100644 --- a/src/app-layer-dnp3.c +++ b/src/app-layer-dnp3.c @@ -98,15 +98,19 @@ enum { * attacks. */ static uint64_t dnp3_max_tx = 32; +/* The maximum number of points allowed per message (configurable). */ +static uint64_t max_points = 16384; + /* Decoder event map. */ SCEnumCharMap dnp3_decoder_event_table[] = { - {"FLOODED", DNP3_DECODER_EVENT_FLOODED}, - {"LEN_TOO_SMALL", DNP3_DECODER_EVENT_LEN_TOO_SMALL}, - {"BAD_LINK_CRC", DNP3_DECODER_EVENT_BAD_LINK_CRC}, - {"BAD_TRANSPORT_CRC", DNP3_DECODER_EVENT_BAD_TRANSPORT_CRC}, - {"MALFORMED", DNP3_DECODER_EVENT_MALFORMED}, - {"UNKNOWN_OBJECT", DNP3_DECODER_EVENT_UNKNOWN_OBJECT}, - {NULL, -1}, + { "FLOODED", DNP3_DECODER_EVENT_FLOODED }, + { "LEN_TOO_SMALL", DNP3_DECODER_EVENT_LEN_TOO_SMALL }, + { "BAD_LINK_CRC", DNP3_DECODER_EVENT_BAD_LINK_CRC }, + { "BAD_TRANSPORT_CRC", DNP3_DECODER_EVENT_BAD_TRANSPORT_CRC }, + { "MALFORMED", DNP3_DECODER_EVENT_MALFORMED }, + { "UNKNOWN_OBJECT", DNP3_DECODER_EVENT_UNKNOWN_OBJECT }, + { "TOO_MANY_POINTS", DNP3_DECODER_EVENT_TOO_MANY_POINTS }, + { NULL, -1 }, }; /* Calculate the next transport sequence number. */ @@ -709,6 +713,7 @@ static int DNP3DecodeApplicationObjects(DNP3Transaction *tx, const uint8_t *buf, uint32_t len, DNP3ObjectList *objects) { int retval = 0; + uint64_t point_count = 0; if (buf == NULL || len == 0) { return 1; @@ -839,6 +844,13 @@ static int DNP3DecodeApplicationObjects(DNP3Transaction *tx, const uint8_t *buf, goto next; } + /* Check if we've exceeded the maximum number of points per message. */ + point_count += object->count; + if (point_count > max_points) { + DNP3SetEventTx(tx, DNP3_DECODER_EVENT_TOO_MANY_POINTS); + goto done; + } + int event = DNP3DecodeObject(header->group, header->variation, &buf, &len, object->prefix_code, object->start, object->count, object->points); @@ -1614,6 +1626,13 @@ void RegisterDNP3Parsers(void) if (ConfGetInt("app-layer.protocols.dnp3.max-tx", &value)) { dnp3_max_tx = (uint64_t)value; } + + /* Parse max-points configuration. */ + if (ConfGetInt("app-layer.protocols.dnp3.max-points", &value)) { + if (value > 0) { + max_points = (uint64_t)value; + } + } } else { SCLogConfig("Parser disabled for protocol %s. " "Protocol detection still on.", proto_name); diff --git a/src/app-layer-dnp3.h b/src/app-layer-dnp3.h index aae07f9c..3e40c7ac 100644 --- a/src/app-layer-dnp3.h +++ b/src/app-layer-dnp3.h @@ -109,6 +109,7 @@ enum { DNP3_DECODER_EVENT_BAD_TRANSPORT_CRC, DNP3_DECODER_EVENT_MALFORMED, DNP3_DECODER_EVENT_UNKNOWN_OBJECT, + DNP3_DECODER_EVENT_TOO_MANY_POINTS, }; /** diff --git a/suricata.yaml.in b/suricata.yaml.in index 1b54e1f2..1514c01a 100644 --- a/suricata.yaml.in +++ b/suricata.yaml.in @@ -1162,6 +1162,7 @@ app-layer: detection-ports: dp: 20000 #max-tx: 32 + #max-points: 16384 # SCADA EtherNet/IP and CIP protocol support enip: -- 2.30.2